<template>
  <div ref="mapRef" class="w-full h-full"></div>
</template>

<script setup lang="tsx">
import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue';
import { useInjectDisabled } from 'ant-design-vue/es/config-provider/DisabledContext.js';
import L from 'leaflet';
import type {
  LatLngExpression,
  LatLngTuple,
  LeafletMouseEventHandlerFn,
  Map,
  Marker,
  MarkerOptions,
} from 'leaflet';
import 'leaflet/dist/leaflet.css';
import './L.TileLayer.NoGap.js';

L.Marker.prototype.options.icon = L.icon({
  iconUrl: '/static/images/marker-icon-2x.png',
  iconSize: [25, 41],
  iconAnchor: [12.5, 41],
});

const contextDisabled = useInjectDisabled();

let map: Map | undefined = undefined;
const mapRef = useTemplateRef('mapRef');

interface Props {
  center?: LatLngExpression;
  markers?: Array<{
    latLng: LatLngExpression;
    options?: MarkerOptions;
  }>;
  lat?: number | string;
  lng?: number | string;
  disabled?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  disabled: undefined,
});

const localDisabled = computed(() => props.disabled ?? contextDisabled.value);

const emits = defineEmits<{
  'update:lat': [number | string];
  'update:lng': [number | string];
  click: [value: LatLngExpression];
}>();

watch(
  () => props.center,
  (newVal) => {
    if (!newVal) return;
    map?.setView(newVal);
  },
);

const _markers: Marker[] = [];
watch(
  () => props.markers,
  (newVal) => {
    if (!newVal) return;
    for (const item of newVal) {
      const marker = L.marker(item.latLng, item.options);
      _markers.push(marker);
      marker.addTo(map!);
    }
  },
);

watch(
  () => [props.lat, props.lng],
  (newVal) => {
    nextTick(() => {
      const [lat, lnt] = newVal;
      if (lat && lnt) {
        map?.setView([Number(lat), Number(lnt)]);
        cleanMarkers?.();
        const marker = L.marker([Number(lat), Number(lnt)]);
        _markers.push(marker);
        marker.addTo(map!);
      }
    });
  },
  {
    immediate: true,
  },
);

const cleanMarkers = () => {
  for (const marker of _markers) {
    map?.removeLayer(marker);
  }
};

const handleClick: LeafletMouseEventHandlerFn = (e) => {
  if (localDisabled.value) return;
  const { lat, lng } = e.latlng;
  cleanMarkers();
  emits('update:lat', typeof props.lat == 'string' ? lat.toString() : lat);
  emits('update:lng', typeof props.lng == 'string' ? lng.toString() : lng);
  emits('click', [lat, lng]);
};

const registerMapEvent = () => {
  map?.on('click', handleClick);
};

onMounted(() => {
  map = L.map(mapRef.value!, {
    maxZoom: 5,
    minZoom: 5,
    zoomControl: false,
    attributionControl: false,
    zoomSnap: 0.2,
    zoomDelta: 0.4,
  }).setView(props.center || [39.913385, 116.394495], 5);
  const layer = L.tileLayer(
    `${location.origin}/static/MAP_XYZ/{z}/{x}/{y}/tile.png`,
  );
  layer.addTo(map);
  registerMapEvent();
});
</script>
